'''
    Copyright (C) 2007  Brian Will

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Use http://code.google.com/p/pigeon-programming-language/issues/list for contacting the author.
'''

'''
    Each function here is what gets called for a Pigeon operator. 
'''

OPERATOR_FUNCTION_NAMES = {
    'add':         'addOperator',
    'sub':         'subOperator',
    'mul':         'mulOperator',
    'div':         'divOperator',
    'mod':         'modOperator',
    'not':         'notOperator',
    'and':         'andOperator',
    'or':          'orOperator',
    'print':       'printOperator',
    'eq':          'eqOperator',
    'id':          'idOperator',
    'gt':          'gtOperator',
    'lt':          'ltOperator',
    'gte':         'gteOperator',
    'lte':         'lteOperator',
    'prompt':      'promptOperator',
    'concat':      'concatOperator',
    'find':        'findOperator',
    'slice':       'sliceOperator',
    'count':       'countOperator',
    'list':        'listOperator',
    'dict':        'dictOperator',
    'get':         'getOperator',
    'set':         'setOperator',
    'cm':          'cmOperator'        # call method operator
}

def addOperator(*args):
    assert len(args) >= 2, 'The add operator requires 2 or more operands.'
    sum = 0
    for arg in args:
        assert type(arg) in (int, float, long), 'The add operator requires numeric operands.'
        sum += arg
    return sum

def andOperator(*args):
    assert len(args) >= 2, 'The and operator requires 2 or more operands.'
    for arg in args:
        assert type(arg) == bool, 'The and operator requires boolean operands.'
        if not arg:
            return False
    return True

def cmOperator(*args):
    obj = args[0]
    assert type(obj) == dict, 'The cm operator expects its first operand to be a dictionary.'
    methodName = args[1]
    assert type(methodName) == str, 'The cm operator expects its second argument to be a string.' 
    method = obj[methodName]
    assert callable(method), 'The cm operator expects the object stored as "' + methodName + '" in the dictionary to be a function.'
    method(*args[2:])

def concatOperator(*args):
    assert len(args) >= 2, 'The concat operator expects 2 or more operands.'
    for arg in args:
        assert type(arg) == str, 'The concat operator expects string arguments.'
    return ''.join(args)

def countOperator(*args):
    assert len(args) == 1, 'Length operator takes one operand.'
    op = args[0]
    assert type(op) in (str, list, dict), 'Length operator\'s operand must be a string, list, or dictionary.'
    return len(op)

def dictOperator(*args):
    numArgs = len(args)
    
    assert numArgs % 2 == 0, 'The dict operator must have an even number of arguments.'
    
    d = {}
    
    i = 0
    while i < numArgs:
        key = args[i]
        assert type(key) == str, 'The keys of a dictionary must be strings.'
        
        d[key] = args[i + 1]
        i += 2
    
    return d

def idOperator(*args):
    assert len(args) >= 2, 'The id operator requires 2 or more operands.'
    firstArg = args[0]
    for arg in args[1:]:
        if firstArg is not arg:
            return False
    return True

def eqOperator(*args):
    assert len(args) >= 2, 'The eq operator requires 2 or more operands.' 
    firstArg = args[0]
    for arg in args[1:]:
        if firstArg != arg:
            return False
    return True    

def findOperator(*args):
    assert len(args) == 2, 'The find operator expects 2 operands.'
    for arg in args:
        assert type(arg) == str, 'The find operator expects string arguments.'
    return args[0].find(args[1])

def getOperator(*args):
    numArgs = len(args)
    assert numArgs >= 2, 'Get operator takes two or more operands.'
    
    obj = args[0]
    
    for index in args[1:]:
        if type(obj) == dict:
            assert type(index) == str, 'In the get operation, a dictionary must be indexed by a string.'
            assert index in obj, 'In the get operation, the index "' + index + '" was not found in the dictionary.' 
        elif type(obj) == list:
            assert type(index) in (int, long), 'In the get operation, a list must be indexed by a number.'
            assert index >= 0, 'In the get operation, the index to a list must be zero or a positive number.'
            assert index < len(obj), 'In the get operation, the index ' + str(index) + ' was out of bounds of the list.'
        else:
            raise 'Get operator can only index dictionaries or lists.'

        obj = obj[index]

    return obj

def gtOperator(*args):
    assert len(args) >= 2, 'The gt operator requires 2 or more operands.'
    firstArg = args[0]
    assert type(firstArg) in (int, float, long), 'The gt operator requries numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The gt operator requries numeric operands.'
        if firstArg <= arg:
            return False
        else:
            firstArg = arg
    return True

def gteOperator(*args):
    assert len(args) >= 2, 'The gte operator requires 2 or more operands.'
    firstArg = args[0]
    assert type(firstArg) in (int, float, long), 'The gte operator requries numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The gte operator requries numeric operands.'
        if firstArg < arg:
            return False
        else:
            firstArg = arg
    return True

def listOperator(*args):
    return list(args)

def ltOperator(*args):
    assert len(args) >= 2, 'The lt operator requires 2 or more operands.'
    firstArg = args[0]
    assert type(firstArg) in (int, float, long), 'The lt operator requries numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The lt operator requries numeric operands.'
        if firstArg >= arg:
            return False
        else:
            firstArg = arg
    return True

def lteOperator(*args):
    assert len(args) >= 2, 'The lte operator requires 2 or more operands.'
    firstArg = args[0]
    assert type(firstArg) in (int, float, long), 'The lte operator requries numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The lte operator requries numeric operands.'
        if firstArg > arg:
            return False
        else:
            firstArg = arg
    return True

def mulOperator(*args):
    assert len(args) >= 2, 'The mul operator requires 2 or more operands.'
    sum = args[0]
    assert type(sum) in (int, float, long), 'The sub operator requires numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The mul operator requires numeric operands.'
        sum *= arg
    return sum

def modOperator(*args):
    assert len(args) >= 2, 'The mod operator requires 2 or more operands.'
    sum = args[0]
    assert type(sum) in (int, float, long), 'The sub operator requires numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The mod operator requires numeric operands.'
        sum %= arg
    return sum

def notOperator(*args):
    assert len(args) == 1, 'The not operator requries 1 operand.'
    arg = args[0]
    assert type(arg) == bool, 'The not operator requries a boolean operand.'
    return not arg

def orOperator(*args):
    assert len(args) >= 2, 'The or operator requires 2 or more operands.'
    for arg in args:
        assert type(arg) == bool, 'The or operator requires boolean operands.'
        if arg:
            return True
    return False

def printOperator(*args):
    assert len(args) >= 1, 'The print operator requires 1 or more operands.'
    for arg in args:
      assert type(arg) == str, 'The print operator requires string operands.'  
    print ''.join(args)

def promptOperator(*args):
    assert len(args) == 0, 'The input operator expects zero operands.'
    return raw_input()

def setOperator(*args):
    numArgs = len(args)
    assert numArgs == 3, 'Set operator takes three operands.'
    
    collection, index, value = args
    
    if type(collection) == dict:
        assert type(index) == str, 'In the set operation, the index to a dictionary must must be a string.'
    elif type(collection) == list:
        assert type(index) in (int, long), 'In the set operation, the index operand must be numeric.'
        assert index >= 0, 'In the set operation, the index to a list must be zero or a positive number.'
        assert index < len(collection), 'In the set operation, the index was out of range.'
    else:
        raise 'Set operator can only modify dictionaries and lists.'    
    
    collection[index] = value

def sliceOperator(*args):
    assert len(args) in (2, 3), 'The slice operator expects 2 or 3 operands.' 
    
    list = args[0]
    assert type(list) in (str, list), 'The slice operator\'s first operand must be a list or string.'
    
    start = args[1]
    assert type(start) == (int, long), 'The slice operator expects the start index to be a number.'
    
    if len(args) == 3:
        end = args[2]
        assert type(end) == (int, long), 'The slice operator expects the end index to be a number.'
    else:
        end = len(list)
        
    return list[start:end]
    
def subOperator(*args):
    assert len(args) >= 2, 'The sub operator requires 2 or more operands.'
    sum = args[0]
    assert type(sum) in (int, float, long), 'The sub operator requires numeric operands.'
    for arg in args[1:]:
        assert type(arg) in (int, float, long), 'The sub operator requires numeric operands.'
        sum -= arg
    return sum